/**
 * @BitmapMovieDir.as
 * 
 * @author sodaChen mail:sujun10#21cn.com
 * @version 1.0
 * <br>Copyright (C), 2013 asFrame.com
 * <br>This program is protected by copyright laws.
 * <br>Program Name:ASFrame
 * <br>Date:2016-6-18
 */
package com.asframe.display.bmp
{
	
	import com.asframe.collections.maps.HashMap;
	import com.asframe.display.data.DirMovieData;
	import com.asframe.display.data.FrameData;
	import com.asframe.display.data.FrameLabelData;
	import com.asframe.lang.Assert;
	import com.asframe.mgr.TimeMgr;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.geom.Point;

	/**
	 * 具备方向性的图片动画播放机制，综合BitmapMovie、BitmapLabelMovieClip、ImageMovieClip的优点重新编写的
	 * todo 会根据实际的使用情况来整合这个几个位图播放体系
	 * @author sodaChen
	 * Date:2016-6-18
	 */
	public class BitmapMovieDir extends Bitmap  //考虑继承Sprite，内部直接使用Bitmap，避免每次都加本身坐标
	{
		/** 方向 **/
		public var dir					:int = -1;
		/** 附加的额外数据 **/
		public var extra				:*;
		/** 动画播放帧频(播放速度，单位是毫秒) **/
		public var frameRate			:int = 100;
		/** 当前正在播放的标签名称（没有则标签则为空） **/
		public var currentLabel			:String;
		public var nextLabel			:String;
		/** 当前动画是否循环播放 **/
		public var isLoop				:Boolean;
		/** 是否正在播放 **/
		public var isPlaying			:Boolean;
		/** 播放结束的回调函数 **/
		public var playComplete			:Function;
		/** 帧回调函数 **/
		public var frameCallBack		:Function;
		
		protected var mStart			:int;
		protected var mEnd				:int;
		
		protected var mLabelMap			:HashMap;
		/** 当前帧数 **/
		protected var mCurrentFrame		:int;
		
		protected var mDirFrames		:Vector.<FrameLabelData>;
		/** 当前播放的动画数据数组（以帧标签为单位） **/
		protected var mFrameLabelData	:FrameLabelData;
		protected var mFrameData		:FrameData;
		////////////这两个属性一般是在FrameLabelData提取出，用于提升性能
		/** 当前播放动画的图像数组数据 **/
		protected var mBitmapDatas		:Vector.<BitmapData>;
		/** 当前播放动画的帧数组数据 **/
		protected var mFrames			:Vector.<FrameData>;
		/** 动画整体数据(无法修改，所有角色共享的) **/
		protected var dirMovieData		:DirMovieData;
		
		protected var localtionX		:int;
		protected var localtionY		:int;
		protected var isPlayOver		:Boolean;
		
		public function BitmapMovieDir(pixelSnapping:String="auto", smoothing:Boolean=false)
		{
			super(null, pixelSnapping, smoothing);
			mLabelMap = new HashMap();
		}
		public function getConfig():*
		{
			return dirMovieData.config;
		}
		public function getLabelConfig():*
		{
			return mFrameLabelData.config;
		}
		/**
		 * 改变图像动画的xy坐标。注意，因为本身有可能会自动设置xy坐标，所以改变改动整体坐标，必须调用这个方法来进行设置
		 * (其实是可以通过增加容器的方式来保证BitmapMovie不被自身改变坐标系) 
		 * @param x
		 * @param y
		 * 
		 */		
		public function setLocation(x:int,y:int):void
		{
			localtionX = x;
			localtionY = y;
		}
		public function getLocation():Point
		{
			return new Point(localtionX,localtionY);
		}
		/**
		 * 设置状态,每次切换方向时都必须调用该方法,方向值必须是ScrollBitmap提供的静态属性值
		 * @param	currWay:当前方向值
		 */ 
		public function changeDir(dir:int):void
		{
			//排除相同方向
			if(this.dir == dir)
				return ;
			
			this.dir = dir;
			
			mFrameLabelData = mDirFrames[dir];
			mBitmapDatas = mFrameLabelData.bitmapDatas;
			mFrames = mFrameLabelData.frameDatas;
			mStart = 0;
			mCurrentFrame = 0;
			mEnd = mBitmapDatas.length;
			scaleX = mFrameLabelData.scale;
			
			render();
		}
		/**
		 * 获取动画的当前帧 
		 * @return 
		 * 
		 */		
		public function getCurrentFrame():int
		{
			return mCurrentFrame + 1;
		}
		public function getTotalFrame():int
		{
			return mBitmapDatas.length;
		}
		/**
		 * 改变动画播放的内容 
		 * @param label 内容标签符号
		 * @param dir 方向
		 * 
		 */		
		public function changeLabel(label:String,dir:int = 0):void
		{
			//这里有可能会出现问题，比如本身设置相同的label，目的是为了重置start的指数
			if(label == null || currentLabel == label)
				return ;
			
			this.currentLabel = label;
			mDirFrames = mLabelMap.get(label);
			//对方向进行归0
			this.dir = -1;
			Assert.notNull(mDirFrames,label + "出现方向帧数据为空");
			changeDir(dir);
		}
		public function setDirMovieData(dirMovieData:DirMovieData):void
		{
			this.dirMovieData = dirMovieData;
			mLabelMap = dirMovieData.labelMap;
			//目前只是纯播放
		}
		/**
		 * 添加一个新的标签内容
		 * @param label 内容标签符号
		 * @param dirFrames 一系列方向的帧数据数组
		 * 
		 */		
		public function addFramebel(label:String,dirFrames:Vector.<FrameLabelData>):void
		{
			//原来已经有了
			if(mLabelMap.hasKey(label))
			{
				throw new Error(label + "已经存在帧数据了");
			}
			mLabelMap.put(label,dirFrames);
			if(this.mFrameLabelData == null)
			{
				//设置默认值
				if(dir == -1)
					changeLabel(label,0);
				else
					changeLabel(label,dir);
			}
		}
		/**
		 * 删除指定的动画内容 
		 * @param label
		 * @return 
		 * 
		 */		
		public function removeFrameLabel(label:String):Vector.<Vector.<FrameLabelData>>
		{
			 return mLabelMap.remove(label);
		}
		/**
		 * 播放Bitmap动画.注意，当设定循环时，不能设置回调函数和nextLabel的参数
		 * @param isLoop 是否循环播放
		 * @param callBack 结束帧数时回调函数
		 * @param label 指定播放的内容，为空则继续播放当前的内容
		 * @param nextLabel label播放完之后自动播放另外nextLabel
		 * 
		 */		
		public function playLabel(label:String = null,
								  dir:int = 0,
								  isLoop:Boolean = true,
								  playComplete:Function = null,
								  nextLabel:String = null):void
		{
			if(isLoop && playComplete != null)
				throw new Error("不能循环的时候具备playComplete参数");
			
			if(isLoop && nextLabel != null)
				throw new Error("不能循环的时候具备nextLabel");
			
			//从0开始算
			if(label != null)
			{
				changeLabel(label,dir);
			}
			if(playComplete != null)
			{
				this.playComplete = playComplete;
			}
			this.isLoop = isLoop;
			this.nextLabel = nextLabel;
			
			play();
			render();
		}
		/**
		 * 播放动画(恢复之前的播放帧，继续播放下去)
		 * @param isLoop 是否循环播放
		 * @param callBack 结束帧数时回调函数
		 * @param label 指定播放的内容，为空则继续播放当前的内容
		 * 
		 */		
		public function play():void
		{
			//还原掉播放状态
			if(isPlayOver)
				isPlayOver = false;
			
			if(isPlaying)
				return ;
			
			isPlaying = true;
			
			if(TimeMgr.hasDelay(update))
				return ;
			
			//添加到心跳列表中
			TimeMgr.addDelay(frameRate,update);
		}
		/**
		 * 停止动画播放（停止在当前动画帧） 
		 * 
		 */		
		public function stop():void
		{
			if(isPlaying)
			{
				isPlaying = false;
				//todo 这里进行删除，容易出现bug
//				TimeMgr.removeDelay(update);
			}
		}
		public function gotoAndPlay(start:int, end:int = -1,isLoop:Boolean = true,label:String = null):void
		{
			if(isLoop && playComplete != null)
				throw new Error("不能循环的时候具备playComplete参数");
			
			changeLabel(label);
			
			this.mStart = start - 1;
			this.mCurrentFrame = mStart;
			if(end != -1)
				this.mEnd = end;
			
			play();
		}
		public function gotoAndStop(stopFrame:int = -1,label:String = null):void
		{
			stop();
			changeLabel(label);
			//渲染当前帧
			if(stopFrame == -1)
				mCurrentFrame = mBitmapDatas.length - 1;
			else
				mCurrentFrame = stopFrame - 1;
			
			render();
		}
		public function render():void
		{
			bitmapData = mBitmapDatas[mCurrentFrame];
			//更新位置
			if(mFrames != null)
			{
				mFrameData = mFrames[mCurrentFrame];
				if(mFrameLabelData.scale == -1)
				{
					x = -(mFrameData.x + localtionX - bitmapData.width);
				}
				else
				{
					x = mFrameData.x + localtionX;
				}
				y = mFrameData.y + localtionY;
				if(mFrameData.extra != null && frameCallBack != null)
				{
					frameCallBack(mFrameData.extra);
				}
			}
		}
		public function update():void
		{
			//不处于播放状态
			if(!isPlaying)
			{
				return ;
			}
			mCurrentFrame++;
			if(mCurrentFrame < mEnd)
			{
				//这里可以为了提升性能，去掉方法，直接写代码。主要是因为调用频繁
				render();
				return ;
			}
			if(isLoop)
			{
				mCurrentFrame = mStart;
				render();
				return ;
			}
			if(nextLabel != null)
			{
				//nextLabel标签一定是循环
				playLabel(nextLabel,dir,true,null);
				changeDir(dir);
				return ;
			}
			//回调
			if(playComplete != null)
			{
				//这里设置属性，是防止回调函数里有再次调用play方法
				isPlayOver = true;
				//这里这样设计的原因，是有可能playComplete回调的时候，还会再次调用play之类的方法进行重新设置playComplete
				var temp:Function = playComplete;
				playComplete = null;
				if(temp.length == 1)
					temp(this);
				else
					temp();
				//这个时候有可能再次调用play方法，所以统一设置停止有点不妥
				if(isPlayOver)
					stop();
			}
			else
			{
				//没有循环，则表示已经播放完了
				stop();
			}
		}
	}
}